{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Python In Practice: 实践为王"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1 文件读写：到此一游"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "观光传送门：\n",
    "https://github.com/BinRoot/Haskell-Data-Analysis-Cookbook/tree/master/Ch01 \n",
    "\n",
    "即使是售价高达$54.99的《Haskell Data Analysis Cookbook》里，第一章也只能讲点平淡无奇的如何读入以下各种形式的文本\n",
    "\n",
    "* TXT,DAT(纯文本，里面的格式你已经有一定的了解)\n",
    "* CSV,TSV(Comma/Tab Separated Values 逗号、Tab分隔值的数据表)\n",
    "* JSON(JavaScript Object Notation，兼容Javascript数据的字符串表达)\n",
    "* XML(eXtensible Markup Language，半结构化数据标记语言，标签在尖括号中)\n",
    "* HTML(Hyper Text Markup Language，网页标记语言，元数据在尖括号中)\n",
    "\n",
    "Python连接各种数据库会在后面的材料中介绍。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "一个简单的csv格式：S0EP4_fakedata.txt\n",
    "\n",
    "* first name , last name , employee number\n",
    "* luke , vanderhart , 1\n",
    "* ryan , neufeld , 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import csv\n",
    "import codecs\n",
    "import numpy as np\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1 文件打开方式\n",
    "\n",
    "如果你确定文件中不会出现中文的话，普通的open就能实现功能了，如果要处理中文的话还需要codecs库。\n",
    "\n",
    "接下来做一下流程\n",
    "\n",
    "* 打开文件\n",
    "* 设置一个空列表\n",
    "* 文件每一行都读取进列表\n",
    "* 碰到文件结尾结束\n",
    "* 关闭文件\n",
    "* 打印内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['first_name,last_name,employee_number\\n', 'luke,vanderhart,1\\n', 'ryan,neufeld,2', '']\n"
     ]
    }
   ],
   "source": [
    "f = open(\"S0EP4_fakedata.txt\",'r')\n",
    "file_content0 = []\n",
    "mark = True\n",
    "while(mark):\n",
    "    file_content0.append(f.readline())\n",
    "    mark = file_content0[-1]\n",
    "f.close()\n",
    "print file_content0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "能看到，最后一个空字符串被读了进来是因为到文件末尾了，前面一些行尾部还有换行符。\n",
    "\n",
    "以及这里我们使用 with as 来代替 f=open() 以及 f.close()，一切都是那么自然，不需要处理文件结尾和关闭文件。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['first_name,last_name,employee_number\\n', 'luke,vanderhart,1\\n', 'ryan,neufeld,2', ''] \n",
      "\n"
     ]
    }
   ],
   "source": [
    "file_content1 = []\n",
    "\n",
    "with open(\"S0EP4_fakedata.txt\") as f:\n",
    "    mark = True\n",
    "    while(mark):\n",
    "        file_content1.append(f.readline())\n",
    "        mark = file_content1[-1]\n",
    "\n",
    "print file_content1,'\\n'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nice!\n",
    "\n",
    "刚才的Readline是为了防止一次读进太大的文件，试试小文件直接读取readlines()："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['first_name,last_name,employee_number\\n', 'luke,vanderhart,1\\n', 'ryan,neufeld,2']\n"
     ]
    }
   ],
   "source": [
    "with open(\"S0EP4_fakedata.txt\") as f:\n",
    "    file_content2 = f.readlines()\n",
    "\n",
    "print file_content2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "中文的挑战：\n",
    "\n",
    "CODECS = COders/DECoderS"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[u'\\u540d,\\u59d3,ID\\n', u'\\u4e8c\\u72d7,\\u738b,1\\n', u'\\u7fe0\\u82b1,\\u674e,2\\n']\n",
      "名,姓,ID\n",
      "二狗,王,1\n",
      "翠花,李,2\n"
     ]
    }
   ],
   "source": [
    "with codecs.open(\"S0EP4_fakedata2.txt\",'r',encoding='utf-8') as f:\n",
    "    file_content3 = f.readlines()\n",
    "\n",
    "print file_content3\n",
    "    \n",
    "for item in file_content3:\n",
    "    print item,\n",
    "\n",
    "with codecs.open(\"S0EP4_fakedata2_w.txt\",\"w\",encoding='utf-8') as f:\n",
    "    for item in file_content3:\n",
    "        f.write(item)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "收尾工作："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    del file_content0\n",
    "    del file_content1\n",
    "    del file_content2\n",
    "    del file_content3\n",
    "except Exception:\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 1.2 非文件式读取CSV"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NumPy是一个可用的选择：\n",
    "\n",
    "这里注意文件使用的字节串与中文字符串的关系。\n",
    "\n",
    "* encode永远用于字符串到字节串转换\n",
    "* decode永远用于字节串到字符串转换"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "二狗 6 二狗 2\n"
     ]
    }
   ],
   "source": [
    "npdata = np.genfromtxt(\"S0EP4_fakedata2.txt\",skip_header=1,dtype=None,delimiter=',')\n",
    "\n",
    "#print npdata\n",
    "\n",
    "print npdata[0][0],len(npdata[0][0]),unicode(npdata[0][0].decode('utf-8')),len(npdata[0][0].decode('utf-8'))\n",
    "\n",
    "#npdata[0][0],npdata[0][0].decode('utf-8')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "继续化简：\n",
    "\n",
    "发现要想使用UTF-8有额外的转码负担，如果你需要在CSV上做一些统计分析工作，建议还是使用Pandas更方便。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    名  姓  ID\n",
      "0  二狗  王   1\n",
      "1  翠花  李   2 <class 'pandas.core.frame.DataFrame'>\n"
     ]
    }
   ],
   "source": [
    "pddata = pd.read_csv(\"S0EP4_fakedata2.txt\",header=0,sep=\",\",encoding=\"utf-8\")\n",
    "\n",
    "print pddata,type(pddata)\n",
    "\n",
    "pddata.to_csv(\"S0EP4_fakedata2_pd.txt\",sep=\"\\t\",index=False,encoding=\"utf-8\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3 直接读写JSON(JavaScript Object Notation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "JSON也是一种常见的数据交换格式，在Web开发、网络数据传输中非常常见。\n",
    "\n",
    "使用json库就可以进行简单的处理，先处理一下从Python数据结构转化为JSON字符串："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{\"subtype\": \"Poison\", \"type\": \"Grass\", \"id\": \"001\", \"name\": \"Bulbasaur\"}, {\"subtype\": \"Poison\", \"type\": \"Grass\", \"id\": \"002\", \"name\": \"Ivysaur\"}, {\"subtype\": \"Poison\", \"type\": \"Grass\", \"id\": \"003\", \"name\": \"Venusaur\"}] \n",
      "<type 'str'> \n",
      "[{u'subtype': u'Poison', u'type': u'Grass', u'id': u'001', u'name': u'Bulbasaur'}, {u'subtype': u'Poison', u'type': u'Grass', u'id': u'002', u'name': u'Ivysaur'}, {u'subtype': u'Poison', u'type': u'Grass', u'id': u'003', u'name': u'Venusaur'}] \n",
      "<type 'list'>\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "from pprint import pprint\n",
    "in_data=[{\n",
    "         \"id\": \"001\",\n",
    "         \"name\": \"Bulbasaur\",\n",
    "         \"type\": \"Grass\",\n",
    "         \"subtype\": \"Poison\"\n",
    "     },\n",
    "     {   \"id\": \"002\",\n",
    "         \"name\": \"Ivysaur\",\n",
    "         \"type\": \"Grass\",\n",
    "         \"subtype\": \"Poison\"\n",
    "     },\n",
    "    {\n",
    "         \"id\": \"003\",\n",
    "         \"subtype\": \"Poison\",\n",
    "         \"name\": \"Venusaur\",\n",
    "         \"type\": \"Grass\"\n",
    "    }]\n",
    "\n",
    "data_string = json.dumps(in_data)          #将一个Python数据结构转换为JSON\n",
    "out_data = json.loads(data_string)         #将JSON编码字符串重现变为一个Python数据结构\n",
    "\n",
    "print data_string,'\\n',type(data_string),'\\n',out_data,'\\n',type(out_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "如果你的JSON要求以文本文件为载体的话"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{u'subtype': u'Poison', u'type': u'Grass', u'id': u'001', u'name': u'Bulbasaur'}, {u'subtype': u'Poison', u'type': u'Grass', u'id': u'002', u'name': u'Ivysaur'}, {u'subtype': u'Poison', u'type': u'Grass', u'id': u'003', u'name': u'Venusaur'}]\n"
     ]
    }
   ],
   "source": [
    "# Writing JSON data\n",
    "with open('S0EP4_jsondata.json', 'w') as f:\n",
    "    json.dump(in_data, f)\n",
    "\n",
    "# Reading data back\n",
    "with open('S0EP4_jsondata.json', 'r') as f:\n",
    "    out_data = json.load(f)\n",
    "\n",
    "print out_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "判断out_data结构，让打印稍微美观一些："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{u'id': u'001',\n",
      "  u'name': u'Bulbasaur',\n",
      "  u'subtype': u'Poison',\n",
      "  u'type': u'Grass'},\n",
      " {u'id': u'002',\n",
      "  u'name': u'Ivysaur',\n",
      "  u'subtype': u'Poison',\n",
      "  u'type': u'Grass'},\n",
      " {u'id': u'003',\n",
      "  u'name': u'Venusaur',\n",
      "  u'subtype': u'Poison',\n",
      "  u'type': u'Grass'}]\n"
     ]
    }
   ],
   "source": [
    "pprint(out_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "网络上常见的美化过的JSON数据："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      "  {\n",
      "    \"subtype\": \"Poison\", \n",
      "    \"type\": \"Grass\", \n",
      "    \"id\": \"001\", \n",
      "    \"name\": \"Bulbasaur\"\n",
      "  }, \n",
      "  {\n",
      "    \"subtype\": \"Poison\", \n",
      "    \"type\": \"Grass\", \n",
      "    \"id\": \"002\", \n",
      "    \"name\": \"Ivysaur\"\n",
      "  }, \n",
      "  {\n",
      "    \"subtype\": \"Poison\", \n",
      "    \"type\": \"Grass\", \n",
      "    \"id\": \"003\", \n",
      "    \"name\": \"Venusaur\"\n",
      "  }\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "print json.dumps(out_data, indent=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2 Get Organized: 包管理、模块组织与工程组织"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 包管理工具\n",
    "\n",
    "发行版 Continuum Analytics(continuum.io/downloads)\n",
    "IDE PyCharm（www.jetbrains.com/pycharm）\n",
    "\n",
    "* conda install numpy（推荐）\n",
    "* pip install numpy（推荐）\n",
    "* easy_install numpy\n",
    "* 源码包： setup.py install\n",
    "* 灵活部署：virtualenv\n",
    "\n",
    "包一般在Python文件夹/lib/site-packages下能够找到。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "###2.2 Import 习惯"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.60943791243\n",
      "1.60943791243\n",
      "10.0\n",
      "1.60943791243\n",
      "1.60943791243\n"
     ]
    }
   ],
   "source": [
    "import scipy\n",
    "print scipy.log(5)\n",
    "\n",
    "import scipy as sp # 推荐！\n",
    "print sp.log(5)\n",
    "\n",
    "import scipy.special as ssp # 推荐！\n",
    "print ssp.comb(5,2)\n",
    "\n",
    "from scipy import log\n",
    "print log(5) # 可能导致名字空间的污染（Namespace Contamination）\n",
    "\n",
    "from scipy import *\n",
    "print log(5) # 强烈不推荐"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "那么我们用有些繁琐的import来简单介绍一下datetime库的功能，感受一下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2015-06-06 11:38\n",
      "2015-05-20 00:00:00 <type 'datetime.datetime'>\n",
      "2015-06-06 10:38\n"
     ]
    }
   ],
   "source": [
    "# 比如我们要获得现在的时间\n",
    "import datetime\n",
    "print datetime.datetime.now().strftime(\"%Y-%m-%d %H:%M\")     #获得当前时间\n",
    "sometime = datetime.datetime.strptime(\"2015-05-20\",\"%Y-%m-%d\")\n",
    "print sometime,type(sometime)                                #获得一个datetime.datetime类型数据\n",
    "print (datetime.datetime.now() - datetime.timedelta(hours=1)).strftime(\"%Y-%m-%d %H:%M\") #获得一小时前的时间"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3 工程组织初探"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python会在lib/site-packages和本文件夹下面自己去寻找可以用的文件，随用随执行。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. Touch the True Programming:  函数，类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "知道如何美观的组织工程之后，接下来了解函数与类，会少量涉及泛函（Functional Programming），后续会介绍functools\n",
    "\n",
    "灵活的使用函数与类是任何一门程序语言的核心。\n",
    "\n",
    "编程的范式：\n",
    "\n",
    "* Procedural Programming\n",
    "* Object-Oriented Programming\n",
    "* Functional Programming\n",
    "\n",
    "Python号称这几种范式全能满足。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.1 函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数：参数进去，结果出来 （它的数学定义也如此）。\n",
    "\n",
    "Procedural Programming: 流程式的写程序，要把你要做的事情写出来：\n",
    "\n",
    "去重："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 5, 8, 10, 12, 15]\n"
     ]
    }
   ],
   "source": [
    "a=[1,2,3,2,5,8,1,3,10,12,15,5]\n",
    "b=list(set(a))\n",
    "print b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数是用来重用你的功能性代码的：如果一件事情你要做多次，那就用函数。\n",
    "\n",
    "### 3.1.1 基本功能\n",
    "\n",
    "不用set去重，发现函数连检查参数类型都不做，这种自由度会不会是以后的坑呢？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 5, 8, 10, 12, 15]\n"
     ]
    }
   ],
   "source": [
    "def dedupe(items):\n",
    "    collect = set()\n",
    "    for item in items:\n",
    "        if item not in collect:\n",
    "            #yield item\n",
    "            collect.add(item)\n",
    "    return collect #和Yield语句二选一\n",
    "a=[1,2,3,2,5,8,1,3,10,12,15,5]\n",
    "b=list(dedupe(a))\n",
    "print b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数可以接受带默认值的参数，称之为keyword arguments。（kwargs）\n",
    "\n",
    "需要注意的是，默认参数应该是不可变的！如果在默认参数中使用了[], {}这种可变类型，很快你的结果就会变得混乱。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "0\n",
      "[3]\n",
      "[3, 4]\n",
      "[3, 4, 2]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "def abs(x=0):\n",
    "    y=x if x >= 0 else -x\n",
    "    return y\n",
    "\n",
    "print abs(-10)\n",
    "print abs()\n",
    "\n",
    "def addlist(x=[]):\n",
    "    x.append(np.random.randint(5))\n",
    "    print x\n",
    "    return None\n",
    "\n",
    "addlist()\n",
    "addlist()\n",
    "addlist()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Point 1:在使用函数前必须定义函数，否则会引发NameError.\n",
    "\n",
    "定义之后直接使用函数是可以的，小括号里面传递合适的参数即可，但是千万不要忘记return，否则："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name 'myabs' is not defined\n",
      "10\n"
     ]
    }
   ],
   "source": [
    "#del myabs\n",
    "\n",
    "try:\n",
    "    print myabs(10)\n",
    "except Exception,e:\n",
    "    print e\n",
    "    \n",
    "def myabs(x):                    \n",
    "    y = x if x >= 0 else -x\n",
    "    return y\n",
    "\n",
    "print myabs(10)               "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Point 2: 使用函数记得return，而且print不能等同于return。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None\n",
      "A man need a code-Bunk\n",
      "<type 'NoneType'>\n"
     ]
    }
   ],
   "source": [
    "def myabs(x):\n",
    "    y = x if x >= 0 else -x\n",
    "    #return y\n",
    "\n",
    "print myabs(-5)\n",
    "\n",
    "def test():\n",
    "    print \"A man need a code-Bunk\"\n",
    "\n",
    "print type(test())  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Point 3:注意return的正确位置\n",
    "\n",
    "第一次碰见return之后，后面就完全不在执行了，相当于break。\n",
    "\n",
    "* 输入4，两者都是碰见if里面的return False\n",
    "* 输入7，因为是质数，所以if内永远不执行，一个是循环体执行1次直接返回True，一个是循环体结束了才返回True\n",
    "* 输入9，一个循环体执行1次直接返回True，一个是执行到i=3的时候返回False\n",
    "\n",
    "用print i输入运行信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False True True\n",
      "False True False\n"
     ]
    }
   ],
   "source": [
    "def is_prime_wrong(n):\n",
    "    for i in xrange(2,n):\n",
    "        #print i,\n",
    "        if n % i == 0:\n",
    "            return False\n",
    "        return True\n",
    "    \n",
    "def is_prime_right(n):\n",
    "    for i in xrange(2,n):\n",
    "        #print i\n",
    "        if n % i == 0:\n",
    "            return False\n",
    "    return True\n",
    "\n",
    "print is_prime_wrong(4),is_prime_wrong(7),is_prime_wrong(9)\n",
    "print is_prime_right(4),is_prime_right(7),is_prime_right(9)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Point 4.最后，在书写函数时，推荐在代码块中附加文档，这个文档按照之前描述的，一般会在三引号中间。\n",
    "\n",
    "* 功能（通过一定的计算return结果）\n",
    "* 参数类型\n",
    "* 计算过程前后的类型变化（例如int -> str,int -> int)\n",
    "* 一小段使用函数的例子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n"
     ]
    }
   ],
   "source": [
    "def myabs(x=0):\n",
    "    \"\"\"\n",
    "    函数：myabs\n",
    "    参数：number -> number\n",
    "    用途：计算数字的绝对值，如果没有给出要求的参数，默认值为0\n",
    "    例子： print myabs(-42)，打印了 42\n",
    "    \"\"\"\n",
    "    y = x if x>= 0 else -x\n",
    "    return y\n",
    "\n",
    "print myabs(-5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Point 5：函数可以调用自身，但是你需要防止无限循环的调用\n",
    "\n",
    "阶乘的概念：\n",
    "\n",
    "$N! = N * (N-1) * ... * 1 = \\prod_{i=1}^N i$\n",
    "\n",
    "程序的执行逻辑是\n",
    "\n",
    "* 如果N不能用int化成整数，会直接报错\n",
    "* 如果N是0,1的话，返回结果1\n",
    "* 如果N是负数，返回空值\n",
    "* 其他情况下，N是大于1的整数，N阶乘返回N乘以N-1的阶乘"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "120\n"
     ]
    }
   ],
   "source": [
    "def myFactorial(N):\n",
    "    intN = int(N)\n",
    "    if intN in [0,1]:\n",
    "        return 1\n",
    "    elif intN<0:\n",
    "        return None\n",
    "    else:\n",
    "        return intN*myFactorial(intN-1)\n",
    "\n",
    "print myFactorial(5.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1.2 Inception of \"Scopes\": 盗变量空间"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Procedural Programming：流程式编程\n",
    "\n",
    "* 函数内层变量叫局部变量，外层叫全局变量\n",
    "\n",
    "* Namespace在多层函数中是各自存在的\n",
    "\n",
    "* 函数在定义和执行阶段可以\"看见\"外面的变量，但是函数设计时，一般不会让内层空间对外层变量进行干涉："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x is You live and learn. At any rate, you live.\n",
      "Changed local x to So long.\n",
      "x is still You live and learn. At any rate, you live.\n"
     ]
    }
   ],
   "source": [
    "x=\"You live and learn. At any rate, you live.\"\n",
    "\n",
    "def func():\n",
    "    print 'x is', x                  #看见了外层x\n",
    "    #x = \"So long.\"                  #使用这句替代exec时会报一个赋值前就使用的错误\n",
    "    exec('x = \\\"So long.\\\"')         #用语法检查不易察觉的方式修改了局部变量x\n",
    "    print 'Changed local x to', x    #打印局部x\n",
    "\n",
    "func()\n",
    "print 'x is still', x                #外层x没有改变"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果你执意去干涉外层，使用global："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('x is', 'You live and learn. At any rate, you live.')\n",
      "('Changed global x to', 'So Long, and Thanks for All the Fish')\n",
      "('Value of x is', 'So Long, and Thanks for All the Fish')\n"
     ]
    }
   ],
   "source": [
    "x = \"You live and learn. At any rate, you live.\"\n",
    "def func():\n",
    "    global x\n",
    "    print('x is', x)\n",
    "    x = \"So Long, and Thanks for All the Fish\"\n",
    "    print('Changed global x to', x)\n",
    "\n",
    "func()\n",
    "print('Value of x is', x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 插播小知识：大蟒蛇Python去哪儿找变量？LEGB Rule告诉你：\n",
    "\n",
    "* Local: 先找locals()，存放当前局部变量的字典\n",
    "* Enclosing: 再找上一层代码块，或者再再上一层代码块（稍后你会知道函数可以包着函数，每个函数都有属于自己那一层空间）\n",
    "* Global: 再找globals()\n",
    "* Built-In: 也就是Python自带的名字空间\n",
    "\n",
    "还没有的话，那就是会告诉你变量没找到，否则，Python最先找到变量在哪儿，就用那里的变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<type 'dict'> <type 'dict'>\n"
     ]
    }
   ],
   "source": [
    "print type(locals()),type(globals())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__main__ True\n"
     ]
    }
   ],
   "source": [
    "print __name__, '__name__' in dir(__builtins__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1.3 新玩具locals( )和globals( )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和变量类似的，函数里面的函数……这个主意怎么样？遗憾的是它仍然会是local的！\n",
    "\n",
    "concrete()内部的abstract()仅限于concrete()使用\n",
    "\n",
    "不管是检查函数还是变量，都可以用locals()字典和globals()字典来检查"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "concrete() called\n",
      "True\n",
      "abstract() called\n",
      "False\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'abstract' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-26-bdc91565636d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      9\u001b[0m \u001b[0mconcrete\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     10\u001b[0m \u001b[0;32mprint\u001b[0m \u001b[0;34m'abstract'\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mglobals\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mabstract\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m: name 'abstract' is not defined"
     ]
    }
   ],
   "source": [
    "def concrete():\n",
    "    \"None\"\n",
    "    print \"concrete() called\"\n",
    "    def abstract():\n",
    "        print \"abstract() called\"\n",
    "    print 'abstract' in locals()\n",
    "    abstract()\n",
    "\n",
    "concrete()\n",
    "print 'abstract' in globals()\n",
    "abstract()               "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当我们发现了新的好玩的东西locals()和globals()可以用更多方法来干涉当前空间中的变量、函数……\n",
    "\n",
    "只要空间里有名字a，不管它是什么，直接删除！（不推荐）然后直接操作globals，发现……"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    del a\n",
    "except Exception:\n",
    "    pass\n",
    "\n",
    "globals()['a'] = 1\n",
    "\n",
    "print a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1.4 凶残的eval( )和exec( )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "如果刚才这些功能还不能满足你，那么教你最后两招（慎用）\n",
    "\n",
    "* evaluate 是对表达式求值\n",
    "* execute 是直接执行字符串里面的语句\n",
    "\n",
    "这两种情况都是简单粗暴的方法，一旦程序出错，IDE很可能不会提示哪里出错了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "9\n"
     ]
    }
   ],
   "source": [
    "a = 3\n",
    "b = eval('a+2')\n",
    "print b\n",
    "\n",
    "e = [2]\n",
    "\n",
    "exec('b'+str(e[0])+' = a**2 ')\n",
    "print b2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有这么多好用的小工具干脆做一个练习吧：\n",
    "\n",
    "给你1-9的数字，在中间随机的插入＋、－、或者什么也不加，最后求和等于100。给出所有的答案。\n",
    "\n",
    "答案之一是123+45-67+8-9=100。\n",
    "\n",
    "如下的短短几行代码方案整合了如下功能：\n",
    "\n",
    "* 函数迭代（调用自身）\n",
    "* re 正则表达式\n",
    "* zip 拉链\n",
    "* format 美观打印\n",
    "* List Comprehension 列表解析\n",
    "* eval 字符串表达式求值\n",
    "* xrange 循环\n",
    "* str 强制类型转换"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  + - +-  123+45-67+8-9\n",
      "  +-+ -   123+4-5+67-89\n",
      "  - - +   123-45-67+89\n",
      "  ----+-  123-4-5-6-7+8-9\n",
      " +++--+   12+3+4+5-6-7+89\n",
      " +-++ ++  12+3-4+5+67+8+9\n",
      " --+-++   12-3-4+5-6+7+89\n",
      "+ -+ +++  1+23-4+56+7+8+9\n",
      "+ -+++ -  1+23-4+5+6+78-9\n",
      "++ -+ -+  1+2+34-5+67-8+9\n",
      "++-+++ +  1+2+3-4+5+6+78+9\n"
     ]
    }
   ],
   "source": [
    "import re\n",
    "def trinary(x):\n",
    "    return trinary(x/3)+str(x%3) if x>=3 else str(x) #自己调用自己、强制类型转换\n",
    "head_str = ''.join([str(i) for i in xrange(1,10)]) #列表解析\n",
    "for i in xrange(3**8): #循环\n",
    "    tail_str = re.sub('0',' ',re.sub('1','+',re.sub('2','-','{0:0>8}'.format(trinary(i)))))+' ' #正则表达式和format\n",
    "    zip_str = re.sub(' ','',''.join([s1+s2 for s1,s2 in zip(head_str,tail_str)])) #正则表达式和zip\n",
    "    if 100==eval(zip_str): #eval\n",
    "        print tail_str,zip_str"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1.5 匿名函数、函数传递"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "等号复制：引用传递 适用于\n",
    "\n",
    "* 变量\n",
    "* 数据结构\n",
    "* 函数\n",
    "* ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "“静态”内存引用：\n",
    "* 小字符串\n",
    "* 小数字\n",
    "\n",
    "普通的引用：\n",
    "* 直接赋值会带来不同ID\n",
    "* 传递引用赋值会带来相同ID"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(4302395312,\n",
       " 4302395312,\n",
       " 4298165816,\n",
       " 4298165816,\n",
       " 4302060680,\n",
       " 4302060680,\n",
       " 4302060872)"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = \"abc\"\n",
    "b = \"abc\"\n",
    "c = 1\n",
    "d = 1\n",
    "e = 4294967295\n",
    "f = e\n",
    "g = 4294967295\n",
    "id(a),id(b),id(c),id(d),id(e),id(f),id(g)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "看了这几个，我觉得我们离“泛函”也就是函数的函数更近了一步。\n",
    "\n",
    "先看看匿名函数也就是lambda的普通用法\n",
    "\n",
    "* def定义有名字的函数\n",
    "* 有名函数起个别名(引用传递)\n",
    "* lambda定义匿名函数并绑定名字\n",
    "* lambda定义匿名函数并直接使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[3, 4, 5] 3 4416226920 3 4416226920\n",
      "3 4416227280 3 4416227160\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "def head(L):\n",
    "    return L[0]\n",
    "\n",
    "headprime = head\n",
    "\n",
    "head2 = lambda x:x[0]\n",
    "head3 = lambda x:x[0]\n",
    "\n",
    "a = range(3,6)\n",
    "\n",
    "print a,head(a),id(head),headprime(a),id(headprime)\n",
    "print head2(a),id(head2),head3(a),id(head3)\n",
    "print (lambda x:x[0])(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，函数也可以作为其他函数的参数！\n",
    "\n",
    "如果你是自学的Python，我猜71.2%的可能性你第一次接触lambda是在排序问题上（当然数字是胡扯的，不要信）\n",
    "\n",
    "sorted第一个参数是字典的iteritems，第二个参数key是一个函数，这个函数用于给iteritems中每一个元素赋予一个值，sorted会根据这个值排序。\n",
    "\n",
    "这里函数就作为参数传递了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('b', 1.2), ('a', 1.0), ('d', 0.9), ('c', 0.8)]\n"
     ]
    }
   ],
   "source": [
    "d = {'a':1.0,'b':1.2,'c':0.8,'d':0.9}\n",
    "print sorted(d.iteritems(),key=lambda x:x[1],reverse=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1.6 高级应用：Decorator\n",
    "\n",
    "如果你有一批变量想统一按一个规则处理，并且需要缩减代码，你需要函数。\n",
    "\n",
    "如果你有一批函数想统一按一个规则处理，并且需要缩减代码，你需要装饰器（Decorator）\n",
    "\n",
    "理清下面2点：\n",
    "\n",
    "* 函数\n",
    "  * 接受参数\n",
    "  * 做点事情\n",
    "  * 返回结果\n",
    "* 装饰器\n",
    "  * 接受函数作为参数\n",
    "  * 做点事情\n",
    "  * 返回一个函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "正常情况下的装饰函数使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Decorated first\n",
      "Main called\n"
     ]
    }
   ],
   "source": [
    "def decorated(func):\n",
    "    print \"Decorated first\"\n",
    "    return func\n",
    "\n",
    "def main_func():\n",
    "    print \"Main called\"\n",
    "\n",
    "dec_main_func=decorated(main_func)\n",
    "\n",
    "dec_main_func()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "装饰器使用：\n",
    "\n",
    "装饰器的语法以@开头，接着是装饰器函数的名字和可选的参数。紧跟着装饰器声明的是被修饰的函数和装饰函数的可选参数。\n",
    "\n",
    "main_func=decorated(main_func)这一句被节省了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Decorated first\n",
      "Function called\n"
     ]
    }
   ],
   "source": [
    "#装饰器定义阶段\n",
    "def decorated(func):\n",
    "    print \"Decorated first\"\n",
    "    return func\n",
    "#装饰器直接使用\n",
    "\n",
    "@decorated\n",
    "def main_func():\n",
    "    print \"Function called\"\n",
    "\n",
    "main_func()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "@那一行的本质相当于 “main_func=test_1(main_func)\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.2 面向对象编程：Object-Oriented Programming"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Python 同样支持面向对象编程。\n",
    "\n",
    "面向对象这个术语最早由艾伦.C.凯提出，他同样是Smalltalk语言的设计者。\n",
    "\n",
    "作为一名有生物学背景的计算机科学家，艾伦在思考编程风格时借鉴了生物中的细胞概念。面向对象的含义来自于它类比的对象：独立且相互交流的生物细胞。某一类细胞在特定环境下对特定化学信号产生特定的蛋白质。而计算机定义类（Class）之后生成的对象（或者有时被称作实例，Instance）也对特定的计算讯息作出特定反映。 \n",
    "\n",
    "* “面向对象”适度地隐藏了内部活动的信息 —— 我不关心你细胞液怎么各种渗透怎么产生电信号……给我蛋白质！\n",
    "* 在函数的基础上，帮助程序员组织代码，并在适当的场合复用代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "简单的例子是，公司雇员需要用一个class进行抽象：\n",
    "\n",
    "* 他们都有姓名，年龄，专业，和员工卡编号\n",
    "* 用姓名作为构建员工对象的变量，而其他几个变量可以进行修改和访问\n",
    "\n",
    "首先定义类，定义一个类的成员，再修改他的变量。\n",
    "\n",
    "类可以起到模板的作用,在创建实例的时候，可以把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的\\_\\_init\\_\\_方法就可以达成这个目标。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class Employee(object):            #括号里指明该类从哪个类继承而来，object是所有类最终会继承的“基本”类\n",
    "    def __init__(self,name):\n",
    "        self.name=name\n",
    "    def set_age(self,age):\n",
    "        self.age=age\n",
    "    def set_major(self,major):\n",
    "        self.major=major\n",
    "    def set_card(self,card):\n",
    "        self.card=card\n",
    "        \n",
    "Francoise=Employee('Francoise')    #  构造一个实例，必须有名字\n",
    "Francoise.set_age('22')\n",
    "Francoise.set_major('Physics')\n",
    "Francoise.set_card('000142')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "尝试访问他的变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "22\n",
      "Physics\n",
      "Francoise\n"
     ]
    }
   ],
   "source": [
    "print Francoise.age\n",
    "print Francoise.major\n",
    "print Francoise.name"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类之间的关系可以用继承实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mathematics\n",
      "5-year Experience Expert\n"
     ]
    }
   ],
   "source": [
    "class Manager(Employee):\n",
    "    qualification=\"5-year Experience Expert\"\n",
    "    \n",
    "Alexandre=Manager('Alexandre')\n",
    "Alexandre.set_age('25')\n",
    "Alexandre.set_major('Mathematics')\n",
    "Alexandre.set_card('00004512')\n",
    "\n",
    "\n",
    "print Alexandre.major\n",
    "print Alexandre.qualification"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "0123456789\n",
    "\n",
    "2620000000  == 7020001000"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
